package com.jeff.springWebTestNG.service;

import java.io.IOException;

import javax.annotation.Resource;
import javax.transaction.SystemException;
import javax.transaction.TransactionManager;

import org.infinispan.AdvancedCache;
import org.infinispan.Cache;
import org.infinispan.CacheException;
import org.infinispan.context.Flag;
import org.infinispan.manager.CacheContainer;
import org.infinispan.manager.DefaultCacheManager;
import org.springframework.stereotype.Service;

import com.jeff.springWebTestNG.infinispan.interceptors.CustomInterceptor1;
import com.jeff.springWebTestNG.infinispan.listeners.OneAsyncListener;
import com.jeff.springWebTestNG.persistence.data.User2;
import com.jeff.springWebTestNG.persistence.ibatis.mapper.IUser2Mapper;

@SuppressWarnings({ "rawtypes", "unchecked" })
@Service
public class PlayInfinispanService implements IPlayInfinispanService {
	private static final String CACHE_MANAGER_CONFIG_LOCATION = "cache-infinispan/default-config.xml";
	private CacheContainer cc;
	public PlayInfinispanService() {
		// TODO Auto-generated constructor stub
		try {
			cc = new DefaultCacheManager(CACHE_MANAGER_CONFIG_LOCATION);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}//or EmbeddedCacheManager, CacheManager already deprecated
	}
	@Resource
	private IUser2Mapper user2Mapper;
	
	
	@Override
	public void checkAvailability() {
		// Since these Map API methods are not reliable(best-effort), 
		//they should only be used for informational or debugging purposes only
		Cache cache = cc.getCache();//use default where no name specified
		System.out.println("The default cache with name as: " + cache.getName());
		
		//put sth to cache
		cache.put("jeff", user2Mapper.getAll().get(0));
		
		//try retrieve the stored object
		User2 jeffUser = (User2) cache.get("jeff");
		System.out.println("The user being stored is: " + jeffUser.getName());
		
		cache.clear();
		System.out.println("After clear the cache size becomes: " + cache.size());
		
		//AdvancedCache provides Flagging api to change behavior of regular operation
		AdvancedCache advCache = cache.getAdvancedCache();
		advCache.withFlags(Flag.CACHE_MODE_LOCAL, Flag.FORCE_SYNCHRONOUS)
			.withFlags(Flag.SKIP_LOCKING)
			.put("jeff", jeffUser);
		cache.clear();
	}
	
	@Override
	public void tryCustomInterceptor() {
		// TODO Auto-generated method stub
		Cache namedCache = cc.getCache("cacheWithCustomInterceptors");
		//check the named cache config correct or not
		AdvancedCache advCache = namedCache.getAdvancedCache();
		advCache.addInterceptor(new CustomInterceptor1(), 1); //must programatically adding interceptor, xml config declare ones doesn't work.
		advCache.withFlags(Flag.SKIP_LOCKING).put(2, "Jeff_The_Great");
		System.out.println("Cache [" + namedCache.getName() + "] has cached " + namedCache.get(2));
		namedCache.clear();
	}
	
	@Override
	public void tryListener() {
		// TODO Auto-generated method stub
		Cache cache = cc.getCache("cacheWithCustomInterceptors");
		cache.addListener(new OneAsyncListener());
		cache.put(15, "Jeff_the_Geek");
		cache.evict(15);
		System.out.println("after eviction");
		cache.clearAsync();
	}
	
	@Override
	public void tryTransaction() {
		// TODO using JTA TransactionManager
		Cache cache = cc.getCache("cacheWithCustomInterceptors");
		AdvancedCache advCache = cache.getAdvancedCache();
		TransactionManager tm = advCache.getTransactionManager();
		try {
			tm.begin();
			
			cache.put(1, "jeff");//get local lock of "1"
			if (true) throw new RuntimeException("Should rollback");
			cache.put(2, "winnie");//get local lock of "2"
			
			tm.commit();//try getting cluster lock of "1" & "2" and then apply the operation 
		} catch (Exception e) {
			//following rollback processing code can be implemented in an Aspect which detecting specific annotation 
			//who marked the methods supporting infinispan transaction, like com.redsector.common.cache.* did
			try {
				tm.rollback();
			} catch (IllegalStateException e1) {
				e1.printStackTrace();
			} catch (SecurityException e1) {
				e1.printStackTrace();
			} catch (SystemException e1) {
				e1.printStackTrace();
			}
			System.out.println(e.getMessage());
		}
		
		System.out.println("Winnie ID is still there? " + cache.containsKey(2));
		System.out.println("And what about Jeff? " + cache.containsKey(1));
		
		cache.clear();
		
		try {
			tm.begin();
			
			cache.put(1, "jeff_1");
			if (true) throw new CacheException("CacheException: Should rollback");
			cache.put(2, "winnie_1");
			
			tm.commit();
		} catch (CacheException e) {
			System.out.println(e.getMessage());
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		
		System.out.println("Winnie_1 ID is still there? " + cache.containsKey(2));
		System.out.println("And what about Jeff_1? " + cache.containsKey(1));//even throw CacheException the transaction still do not auto rollback
		
		
	}
}
